#include <stdio.h>
#include <stdlib.h>
#include <time.h>
#include <pthread.h>
#include <gmp.h>
#include <time.h>

unsigned long int ITER,THREADS;
mpf_t Pin; /* quantidade de pontos dentro da área do círculo */
mpf_t Ptotal; /* quantidade total de pontos */

pthread_mutex_t pts_mux = PTHREAD_MUTEX_INITIALIZER; /* mutex para controlar o acesso a variável Pin */
pthread_mutex_t iter_mux = PTHREAD_MUTEX_INITIALIZER; /* mutex para controlar o acesso a variável Ptotal */

void *mc() {
  int cmp = -1;
  mpf_t x, y, p;
  gmp_randstate_t state;
  
  /* Inicialização das variáveis */
  mpf_init(x);
  mpf_init(y);
  mpf_init(p);
  gmp_randinit_default(state);
  gmp_randseed_ui(state,time(NULL));

  while(cmp < 0)
  {
    /* Geração de x e y aleatórios */
    mpf_urandomb(x,state,100);
    mpf_urandomb(y,state,100);
    
    /* x^2 */
    mpf_pow_ui(x,x,2);
    /* y^2 */
    mpf_pow_ui(y,y,2);

    /* x^2 + y^2 */
    mpf_add(p,x,y);

    /* x^2 + y^2 <= 1.0 */
    cmp = mpf_cmp_d(p,1.0);
    if(cmp <= 0) {
      pthread_mutex_lock(&pts_mux);
      mpf_add_ui(Pin,Pin,1);      
      pthread_mutex_unlock(&pts_mux);
    }

    pthread_mutex_lock(&iter_mux);
    mpf_add_ui(Ptotal,Ptotal,1);
    pthread_mutex_unlock(&iter_mux);

    cmp = mpf_cmp_ui(Ptotal,ITER);
  }

  /* Libera o espaço ocupado pelas variáveis */
  mpf_clear(x);
  mpf_clear(y);
  mpf_clear(p);
  gmp_randclear(state);

  /* finaliza a thread */
  pthread_exit(NULL);  
}

int main(int argc, char *argv[]) {
  pthread_t *threads;
  long i;
  int t;
  mpf_t PI;
  FILE *arq = fopen("pi_par.txt", "w+");
  clock_t begin, end;
  
   mpf_set_default_prec(40000000);

  begin = clock();

  /* Inicialização das variáveis */
  mpf_init(Pin);
  mpf_init(Ptotal);
  mpf_init(PI);

  if(argc != 3) 
  {
    printf("usage: %s n_threads n_interations\n",argv[0]);
    printf("n_threads: number of threads\n");
    printf("n_iterations: number of iterations\n");
  } 
  else
  {
    sscanf(argv[1],"%ld",&THREADS);
    sscanf(argv[2],"%ld",&ITER);
  }

  /* cria as threads */
  threads = (pthread_t *)malloc(THREADS*sizeof(pthread_t));
  for(i=0;i<THREADS;i++) {
    t = pthread_create(&threads[i],NULL,mc,NULL);
    if(t) {
      printf("Erro na criação da thread.\n");
      exit(-1);
    } 
  }

  /* espera até as threads terminarem */
  for(i=0;i<THREADS;i++)
     pthread_join(threads[i], NULL);

  /* PI = Pin/Ptotal */
  mpf_div(PI,Pin,Ptotal);
  
  /* PI = 4 * Pin/Ptotal */
  mpf_mul_ui(PI,PI,4);
  
  /* imprime o valor de PI no arquivo pi_par.txt */
  if(arq != NULL)
    mpf_out_str(arq, 10, 0, PI);
  end = clock();
  
  /* Libera o espaço ocupado pelas variáveis */
  mpf_clear(Pin);
  mpf_clear(Ptotal);
  mpf_clear(PI);
  pthread_mutex_destroy(&pts_mux);
  pthread_mutex_destroy(&iter_mux);
  
  printf("\nTempo do calculo: %lf segundos\n",(double)((end - begin)/CLOCKS_PER_SEC));
  
  fclose(arq);
    
  return 0;
}

